package p.ithorns.framework.feign.enhance;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.support.AopUtils;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import org.springframework.cloud.openfeign.FeignClient;
import p.ithorns.framework.feign.common.FeignErrorAdvice;

import java.lang.reflect.Method;

/**
 * CglibProxy
 *
 * @author Ht.L
 * @date 2024-01-21 22:02
 * @since 1.0.0
 */
@FeignClient
public class FeignSvcCglibProxy implements MethodInterceptor {

    private final static Logger log = LoggerFactory.getLogger(FeignSvcCglibProxy.class);

    private final Object target;

    public FeignSvcCglibProxy(Object target) {
        this.target = target;
    }

    @Override
    public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        Class<?> targetClass = AopUtils.getTargetClass(proxy);
        if (method.getName().equals("toString")) {
            return targetClass.toString();
        }
        if (method.getName().equals("equals")) {
            return method.invoke(targetClass, args);
        }
        if (method.getName().equals("hashCode")) {
            return targetClass.hashCode();
        }

        return method.invoke(target, args);
    }

    public static boolean isCglibProxy(Object obj) {
        return (obj != null && obj.getClass().getName().contains("$$EnhancerByCGLIB$$"));
    }


    /**
     * Cglib动态代理类
     * 生成动态代理对象时, 假设被代理类BaseClass有方法method1,
     * 则在动态代理类中, 会对BaseClass的method1方法生成两个代理方法, 一个为CGLIB$method1, 内容类似于:
     * final void CGLIB$method1()  throws Throwable {
     *   super.finalize();
     * }
     *
     * 另一个为method1,其中method1调用拦截器的intercept方法, 内容类似于:
     * public final void method1(){
     * 		...
     *         MethodInterceptor interceptor= this.CGLIB$CALLBACK_0;
     *         ...
     *         ...
     *         //调用拦截器，this就是当前的代理类
     *         interceptor.intercept(this, CGLIB$method1$0$Method, CGLIB$emptyArgs,CGLIB$method1$0$Proxy);
     *         ...
     * }
     *
     * 代理类中对被代理类的每个可见方法都创建了一个methodProxy对象,
     * 该对象是通过调用MethodProxy.create方法创建的代理类, 其中存储了被代理类的Class f2, 动态代理类的Class f1, 以及上面两个方法的index
     *
     * # MethodProxy#invoke
     * f1是被代理类的FastClass, i1是method1在被代理类中对应的Index
     * 将参数obj转型为f1对应的类型,
     * 按照i1调用obj对象的method1方法
     * 如果obj不是代理对象,则相当于直接调用原对象的method1
     * 否则在就是调用动态代理类的method1方法, 会在其中再次调用拦截器
     *
     * 如果传入的obj是被代理类的对象, 那么在method1中再调用其他方法是不会触发拦截的, spring的动态代理就是这样做的
     * 如果传入的obj是代理对象, 会递归死循环调用method1的拦截器, 触发报错
     *
     * # MethodProxy#invokeSuper
     * f2是动态代理类的FastClass, i2对应CGLIB$method1
     * 将obj参数强制转型为f2, obj只能传入动态代理类的对象, 否则会转型失败
     * 然后调用传入按照i2调用obj对象的CGLIB$method1方法
     *
     * 更通俗的理解:
     * invokeSuper调用的是被代理类的方法, 但只有代理类才存在基类, 必须使用代理类作为obj参数调用
     * invoke调用的是增强方法, 必须使用被代理类的对象调用, 使用代理类会造成OOM
     */

}